﻿using System;
using System.Collections.Generic;

namespace Subkismet.PasswordGenerator
{
    /// <summary>
    /// A random password generator.
    /// </summary>
    public class PasswordGenerator
    {
        #region Properties

        /// <summary>
        /// Gets or sets the minimum number of upper case characters to use for password generation.
        /// </summary>
        public int MinimumUpperCaseChars { get; set; }

        /// <summary>
        /// Gets or sets the minimum number of lower case characters to use for password generation.
        /// </summary>
        public int MinimumLowerCaseChars { get; set; }

        /// <summary>
        /// Gets or sets the minimum number of numeric characters to use for password generation.
        /// </summary>
        public int MinimumNumericChars { get; set; }

        /// <summary>
        /// Gets or sets the minimum number of special characters to use for password generation.
        /// </summary>
        public int MinimumSpecialChars { get; set; }

        /// <summary>
        /// Gets or sets the type of character to use to fill the rest of the password.
        /// </summary>
        public CharType FillRest { get; set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// Generates a random password string of specified length.
        /// </summary>
        /// <param name="length">The length of the password string to generate.</param>
        /// <returns>The password string.</returns>
        public string GeneratePassword(int length)
        {
            int sum = this.MinimumUpperCaseChars + this.MinimumLowerCaseChars +
                this.MinimumNumericChars + this.MinimumSpecialChars;

            if (length < sum)
                throw new ArgumentException("length parameter must be valid!");

            List<char> chars = new List<char>();

            if (this.MinimumUpperCaseChars > 0)
                chars.AddRange(GetUpperCasePasswordChars(this.MinimumUpperCaseChars));

            if (this.MinimumLowerCaseChars > 0)
                chars.AddRange(GetLowerCasePasswordChars(this.MinimumLowerCaseChars));

            if (this.MinimumNumericChars > 0)
                chars.AddRange(GetNumericPasswordChars(this.MinimumNumericChars));

            if (this.MinimumSpecialChars > 0)
                chars.AddRange(GetSpecialPasswordChars(this.MinimumSpecialChars));

            int restLength = length - chars.Count;

            switch (this.FillRest)
            {
                case (CharType.UpperCase):
                    chars.AddRange(GetUpperCasePasswordChars(restLength));
                    break;
                case (CharType.LowerCase):
                    chars.AddRange(GetLowerCasePasswordChars(restLength));
                    break;
                case CharType.Numeric:
                    chars.AddRange(GetNumericPasswordChars(restLength));
                    break;
                case CharType.Special:
                    chars.AddRange(GetSpecialPasswordChars(restLength));
                    break;
                default:
                    chars.AddRange(GetLowerCasePasswordChars(restLength));
                    break;
            }

            return GeneratePasswordFromList(chars);
        }

        #endregion

        #region Private Methods

        private List<char> GetUpperCasePasswordChars(int count)
        {
            List<char> result = new List<char>();

            Random random = new Random();

            for (int index = 0; index < count; index++)
                result.Add(Char.ToUpper(Convert.ToChar(random.Next(65, 90))));

            return result;
        }

        private List<char> GetLowerCasePasswordChars(int count)
        {
            List<char> result = new List<char>();

            Random random = new Random();

            for (int index = 0; index < count; index++)
                result.Add(Char.ToLower(Convert.ToChar(random.Next(97, 122))));

            return result;
        }

        private List<char> GetNumericPasswordChars(int count)
        {
            List<char> result = new List<char>();

            Random random = new Random();

            for (int index = 0; index < count; index++)
                result.Add(Convert.ToChar(random.Next(0, 9).ToString()));

            return result;
        }

        private List<char> GetSpecialPasswordChars(int count)
        {
            List<char> result = new List<char>();

            Random random = new Random();

            for (int index = 0; index < count; index++)
                result.Add(Char.ToLower(Convert.ToChar(random.Next(35, 38))));

            return result;
        }

        private string GeneratePasswordFromList(List<char> chars)
        {
            string result = string.Empty;

            Random random = new Random();

            while (chars.Count > 0)
            {
                int randomIndex = random.Next(0, chars.Count);
                result += chars[randomIndex];
                chars.RemoveAt(randomIndex);
            }

            return result;
        }

        #endregion
    }
}
